package org.emf.plugins.tomcatlauncher.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

import org.emf.plugins.tomcatlauncher.server.KyahRequest.Command;
import org.emf.plugins.tomcatlauncher.server.KyahResponse.Token;

/**
 * Kyah Server.
 */
public class KyahServer {

    /** Server socket. */
    private ServerSocket serverSocket;
    /** Client socket trying to establish communication. */
    private Socket clientSocket;
    /** Indicates the server status. */
    private Status status = Status.IDLE;

    /**
     * Starts the server.
     * @param listener listen to incoming commands in the Kyah server.
     * @throws IOException if communication fails.
     */
    public void start(CommandListener listener) throws IOException {
        try {
            serverSocket = new ServerSocket(CommChannel.SERVER_PORT);
            while ((serverSocket != null && !serverSocket.isClosed()) && (clientSocket = serverSocket.accept()) != null) {
                CommChannel channel = new CommChannel(
                        new BufferedReader(new InputStreamReader(clientSocket.getInputStream())),
                        new PrintWriter(clientSocket.getOutputStream()));
                if (Status.IDLE.equals(status)) {
                    KyahResponse response = new KyahResponse(Token.ESTABLISHED, "Success");
                    channel.sendResponse(response);
                    status = Status.ESTABLISHED;
                }
                while (channel.readCommand()) {
                    status = Status.WORKING;
                    Command command = channel.getRequest().getCommand();
                    boolean isSuccess = listener.onCommand(channel.getRequest().getCommand());
                    KyahResponse response = createResponse(command, isSuccess);
                    channel.sendResponse(response);
                    if (Token.STOP_OK.equals(response.getToken())) {
                        channel.close();
                        stop();
                        break;
                    }
                }
                status = Status.IDLE;
            }
        } catch (Exception e) {
            throw new IOException("Command not recognized", e);
        } finally {
           stop();
        }
    }

    /**
     * Stops server.
     * @throws IOException if not possible to stop.
     */
    private void stop() throws IOException {
        if (clientSocket != null && !clientSocket.isClosed()) {
            clientSocket.close();
        }
        if (serverSocket != null && !serverSocket.isClosed()) {
            serverSocket.close();
        }
    }

    /**
     * Indicates if server is up and running.
     * @return TRUE if server is up and running, FALSE otehrwise.
     */
    public boolean isUp() {
        return serverSocket != null && !serverSocket.isClosed();
    }

    /**
     * Indicates the server status.
     */
    private enum Status {
        /** Server is started but no communication has been initiated. */
        IDLE,
        /** Communication has been established. */
        ESTABLISHED,
        /** Server is busy. Client and server are talking to each other. */
        WORKING,
    }

    /*
     * Creates the response as a result of the command sent and processed.
     */
    private KyahResponse createResponse(Command command, boolean isSuccess) {
        switch (command) {
        case STOP:
            if (isSuccess) {
                return new KyahResponse(Token.STOP_OK, "Success");
            }
            return new KyahResponse(Token.FAILURE, "Stop has failed.");
        case START:
            if (isSuccess) {
                return new KyahResponse(Token.START_OK, "Success");
            }
            return new KyahResponse(Token.FAILURE, "Start has failed.");
        default:
            return new KyahResponse(Token.FAILURE, "Command not recognized.");
        }
    }
}
